/*-------------------------------------------------------------------------
 *
 * nodeBitmapAnd.c
 *	  处理 BitmapAnd 节点的例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeBitmapAnd.c
 *
 *-------------------------------------------------------------------------
 */
/* 接口例程
 *		ExecInitBitmapAnd	- 初始化 BitmapAnd 节点
 *		MultiExecBitmapAnd	- 从节点检索结果位图
 *		ExecEndBitmapAnd	- 关闭 BitmapAnd 节点
 *		ExecReScanBitmapAnd - 重新扫描 BitmapAnd 节点
 *
 *	 注意事项
 *		BitmapAnd 节点不使用它们的左右子树，而是维护一个子计划的列表，
 *		很像 Append 节点。然而，逻辑要简单得多，因为我们无需处理
 *		前向/后向执行。
 */

#include "postgres.h"

#include "executor/execdebug.h"
#include "executor/nodeBitmapAnd.h"


/* ----------------------------------------------------------------
 *		ExecBitmapAnd
 *
 *		形式合规的存根
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecBitmapAnd(PlanState *fc_pstate)
{
	elog(ERROR, "BitmapAnd node does not support ExecProcNode call convention");
	return NULL;
}

/* ----------------------------------------------------------------
 *		ExecInitBitmapAnd
 *
 *		开始 BitmapAnd 节点的所有子扫描。
 * ----------------------------------------------------------------
 */
BitmapAndState *
ExecInitBitmapAnd(BitmapAnd *fc_node, EState *fc_estate, int fc_eflags)
{
	BitmapAndState *fc_bitmapandstate = makeNode(BitmapAndState);
	PlanState **fc_bitmapplanstates;
	int			fc_nplans;
	int			fc_i;
	ListCell   *fc_l;
	Plan	   *fc_initNode;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * 设置空的子计划状态向量
	 */
	fc_nplans = list_length(fc_node->bitmapplans);

	fc_bitmapplanstates = (PlanState **) palloc0(fc_nplans * sizeof(PlanState *));

	/*
	 * 为我们的 BitmapAnd 节点创建新的 BitmapAndState
	 */
	fc_bitmapandstate->ps.plan = (Plan *) fc_node;
	fc_bitmapandstate->ps.state = fc_estate;
	fc_bitmapandstate->ps.ExecProcNode = fc_ExecBitmapAnd;
	fc_bitmapandstate->bitmapplans = fc_bitmapplanstates;
	fc_bitmapandstate->nplans = fc_nplans;

	/*
	 * 对每个要执行的计划调用 ExecInitNode，并将结果保存到
	 * 数组 "bitmapplanstates" 中。
	 */
	fc_i = 0;
	foreach(fc_l, fc_node->bitmapplans)
	{
		fc_initNode = (Plan *) lfirst(fc_l);
		fc_bitmapplanstates[fc_i] = ExecInitNode(fc_initNode, fc_estate, fc_eflags);
		fc_i++;
	}

	/*
	 * 杂项初始化
	 *
	 * BitmapAnd 计划没有表达式上下文，因为它们从不调用
	 * ExecQual 或 ExecProject。它们也不需要任何元组槽。
	 */

	return fc_bitmapandstate;
}

/* ----------------------------------------------------------------
 *	   MultiExecBitmapAnd
 * ----------------------------------------------------------------
 */
Node * MultiExecBitmapAnd(BitmapAndState *fc_node)
{
	PlanState **fc_bitmapplans;
	int			fc_nplans;
	int			fc_i;
	TIDBitmap  *fc_result = NULL;

	/* 必须提供我们自己的仪器支持 */
	if (fc_node->ps.instrument)
		InstrStartNode(fc_node->ps.instrument);

	/*
	 * 从节点获取信息
	 */
	fc_bitmapplans = fc_node->bitmapplans;
	fc_nplans = fc_node->nplans;

	/*
	 * 扫描所有子计划并对它们的结果位图进行 AND 运算
	 */
	for (fc_i = 0; fc_i < fc_nplans; fc_i++)
	{
		PlanState  *fc_subnode = fc_bitmapplans[fc_i];
		TIDBitmap  *fc_subresult;

		fc_subresult = (TIDBitmap *) MultiExecProcNode(fc_subnode);

		if (!fc_subresult || !IsA(fc_subresult, TIDBitmap))
			elog(ERROR, "unrecognized result from subplan");

		if (fc_result == NULL)
			fc_result = fc_subresult; /* 第一个子计划 */
		else
		{
			tbm_intersect(fc_result, fc_subresult);
			tbm_free(fc_subresult);
		}

		/*
		 * 如果在任何阶段我们有完全空的位图，我们可以退出，而
		 * 不评估其余子计划，因为进行 AND 运算无法再改变结果。
		 * （注意：indxpath.c 按选择性对子计划进行排序的事实应使
		 * 这种情况更可能发生。）
		 */
		if (tbm_is_empty(fc_result))
			break;
	}

	if (fc_result == NULL)
		elog(ERROR, "BitmapAnd doesn't support zero inputs");

	/* 必须提供我们自己的仪器支持 */
	if (fc_node->ps.instrument)
		InstrStopNode(fc_node->ps.instrument, 0 /* XXX */ );

	return (Node *) fc_result;
}

/* ----------------------------------------------------------------
 *		ExecEndBitmapAnd
 *
 *		关闭 BitmapAnd 节点的子扫描。
 *
 *		不返回任何有趣的内容。
 * ----------------------------------------------------------------
 */
void ExecEndBitmapAnd(BitmapAndState *fc_node)
{
	PlanState **fc_bitmapplans;
	int			fc_nplans;
	int			fc_i;

	/*
	 * 从节点获取信息
	 */
	fc_bitmapplans = fc_node->bitmapplans;
	fc_nplans = fc_node->nplans;

	/*
	 * 关闭每个已初始化的子扫描
	 */
	for (fc_i = 0; fc_i < fc_nplans; fc_i++)
	{
		if (fc_bitmapplans[fc_i])
			ExecEndNode(fc_bitmapplans[fc_i]);
	}
}

void ExecReScanBitmapAnd(BitmapAndState *fc_node)
{
	int			fc_i;

	for (fc_i = 0; fc_i < fc_node->nplans; fc_i++)
	{
		PlanState  *fc_subnode = fc_node->bitmapplans[fc_i];

		/*
		 * ExecReScan 不知道我的子计划，所以我必须自己进行
		 * 变化参数的信号传递。
		 */
		if (fc_node->ps.chgParam != NULL)
			UpdateChangedParamSet(fc_subnode, fc_node->ps.chgParam);

		/*
		 * 如果子节点的 chgParam 不是 null，则计划将通过
		 * 第一个 ExecProcNode 重新扫描。
		 */
		if (fc_subnode->chgParam == NULL)
			ExecReScan(fc_subnode);
	}
}
